#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

#define NUM 1024 
#define SIZE 32 
#define SEP " "

//bufeer: 缓冲区

char g_myval[64];

//保存完整的命令行字符串 
char cmd_line[NUM] ;

//保存打散后的字符串 
char *g_argv[SIZE]; 


//定义宏

#define INPUT_REDTR 1 
#define OUTPUT_REDIR 2 
#define APPEND_REDIR 3 
#define NONE_REDIR 0 

int redir_status = NONE_REDIR;

char *CheckRedir(char *start)
{
  assert(start);
  char* end = start + strlen(start) - 1 ; //类似迭代器,但指向的是最后一个元素/字符
  while(end >= start)
  {
    if(*end == '>')
    {
      if(*(end-1) == '>')
      {
        redir_status = APPEND_REDIR ; 
        *(end - 1) = '\0' ;
        end++ ; 
        break ;
      }
      redir_status = OUTPUT_REDIR ; 
      *end = '\0';
      end++ ;
      break;
    }
    else if(*end == '<')
    {
      redir_status = INPUT_REDTR;
      *end = '\0';
      end++;
      break; 
    }
    else
    {
      end--;
    }
  }

  
  if(end >= start) //保证 << 出现在规范位置 
  {
    return end; //end 此时指向要打开的文件名称第一个字符的位置，也就代表了要打开的文件名
  }
  else
  {
    return NULL ; 
  }
}

//shell主体
int main()
{
  //environ是一个全局变量，它是一个字符指针数组，用于存储当前进程的环境变量列表。
  extern char** environ;

  // shell执行命令后不退出
  while(1)
  {
    //打印提示信息
    printf("[root@localhost myshell]#");
    fflush(stdout);
    memset(cmd_line, '\0', sizeof cmd_line ); //初始化
    
    //获取用户输入指令 
    
    //从stdin中读取 sizeof cmd_line 大小的内容进入cmd_line,遇到结尾停止读取 
    if(fgets(cmd_line, sizeof cmd_line , stdin) == NULL)
    {
      continue ;
    }

    cmd_line[strlen(cmd_line) - 1 ] = '\0';

    // 分析是否有重定向 
    
    char* sep = CheckRedir(cmd_line);

    //将读到的字符串，以空格为标记打散，方便指令的执行
    g_argv[0] = strtok(cmd_line , SEP) ; // 第一次调用strtok 
    int index = 1; 
    while(g_argv[index++] = strtok(NULL,SEP)) ; //后续循环调用  // 如果没有更多可以分割则返回NULL结束循环

    // 支持export 指令
    if(strcmp (g_argv[0] , "export") == 0 && g_argv[1] != NULL)
    {
      strcpy(g_myval, g_argv[1]);
      int ret = putenv(g_myval); //putenv是系统接口

      if(ret ==  0)
      {
        printf(" %s export success \n", g_argv[1]) ;
      }
      continue; 
    }
     
    //内置命令 
    if(strcmp(g_argv[0], "cd") == 0)
    {
      if(g_argv[1] != NULL) 
      {
        chdir(g_argv[1]); // 切换当前工作目录/即进程所在目录 的系统接口
      }
      continue ; 
    }
    
    // fork 执行其他命令
    pid_t id = fork();

    //重定向指令
    if(id == 0) // 子进程执行
    {
      if(sep != NULL) // 返回值为空说明发生了重定向
      {
        int fd = -1; // ???
        
        switch(redir_status)
        {
          case INPUT_REDTR: 
            fd = open(sep, O_RDONLY );
            dup2(fd, 0); //new位置只会是0/1因为只会是输入/输出重定向
            break;
          case OUTPUT_REDIR:
            fd = open(sep, O_WRONLY | O_TRUNC | O_CREAT , 0666);
            dup2(fd, 1);
            break;
          case APPEND_REDIR:
            fd = open(sep,O_WRONLY | O_APPEND | O_CREAT , 0666); //0666为文件默认权限 //O_CREAT 文件不存在则创建文件
            dup2(fd, 1);
            break;
          default:
            printf("错误\n");
            break;
        }
      }

       execvp(g_argv[0],g_argv);                                                                                                                                       exit(1);
    }
    int status = 0 ;
    pid_t ret = waitpid(id , &status,0);
    if(ret > 0)
    {
      printf("exit code: %d\n" , WEXITSTATUS(status)); //如果子进程正常退出，返回子进程的退出状态码。
    }  
  }
  

}



